package com.tanjun.common.utils;


import com.mybatisflex.core.paginate.Page;
import com.tanjun.common.response.PageResponse;
import lombok.SneakyThrows;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public final class  ConvertUntil {

    private static final Map<String, BeanCopier> beanCopierMap= Collections.synchronizedMap(new HashMap<>());

    /**
     * 对象拷贝（浅拷贝）
     * @param sourceCollection 原始对象
     * @param targetClass 目标类
     * @param <T> 原始对象类型
     * @param <M> 目标对象类型
     * @return
     */
    public static <T,M> Collection<M> lowConvert(Collection<T> sourceCollection, Class<M> targetClass){
        if(sourceCollection==null){
            return null;
        }
        if(sourceCollection instanceof List) {
            return sourceCollection.stream().map(d -> lowConvert(d, targetClass)).collect(Collectors.toList());
        }
        if(sourceCollection instanceof Set) {
            return sourceCollection.stream().map(d -> lowConvert(d, targetClass)).collect(Collectors.toSet());
        }
        throw new RuntimeException("Collection只支持set和list");
    }

    @SneakyThrows
    public static <T,M> M lowConvert(T source, Class<M> targetClass){
        if(source==null){
            return null;
        }
        String key=source.getClass().toString()+":"+targetClass.toString();
        BeanCopier beanCopier=null;
        if(beanCopierMap.containsKey(key)){
            beanCopier=beanCopierMap.get(key);
        }
        else {
            synchronized (ConvertUntil.class){
                if(!beanCopierMap.containsKey(key)) {
                    beanCopier = BeanCopier.create(source.getClass(), targetClass, true);
                    beanCopierMap.put(key,beanCopier);
                }
                else {
                    beanCopier=beanCopierMap.get(key);
                }
            }
        }
        M re=targetClass.newInstance();
        beanCopier.copy(source,re,new TypeConvert());
        return re;
    }

    private static class TypeConvert implements Converter {
        @SneakyThrows
        @Override
        public Object convert(Object value, Class target, Object context) {
            if(value==null){
                return null;
            }
            String s=value.toString();
            if(target.equals(int.class)||target.equals(Integer.class)){
                return Integer.parseInt(s);
            }
          else   if(target.equals(float.class)||target.equals(Float.class)){
                return Float.parseFloat(s);
            }
            else  if(target.equals(double.class)||target.equals(Double.class)){
                return Double.parseDouble(s);
            }
            else if(target.equals(long.class)||target.equals(Long.class)){
                return Long.parseLong(s);
            }
            else if(target.equals(Boolean.class)||target.equals(boolean.class)){
                return Boolean.parseBoolean(s);
            }

            else  if (target.equals(BigDecimal.class)) {
                if (s!=null&& !s.equals("NaN")) {
                    return new BigDecimal(s);
                }
            }
            return value;
        }

    }


   private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder()
           .useAutoMapping(true)
           .mapNulls(true)
           .build();

    /**
     * 默认字段实例集合
     */
    private static Map<String, MapperFacade> CACHE_MAPPER_FACADE_MAP = new ConcurrentHashMap<>();

    private static Map<String, MapperFacade> getCacheMapperFacadeMap() {
        return CACHE_MAPPER_FACADE_MAP;
    }

    public static <T,M> M convert(T source, Class<M> targetClass){
        if(source==null){
            return null;
        }
        MapperFacade mapperFacade=getMapperFacade(getMapperKey(source.getClass(),targetClass));
        return mapperFacade.map(source,targetClass);
    }

    public static <T, M> List<M> convert(List<T> list, Class<M> targetClass) {
            if(list==null){
                return null;
            }
            if(CollectionUtils.isEmpty(list)) {
                return Collections.emptyList();
            }
            String key=getMapperKey(list.get(0).getClass(),targetClass);
            return  getMapperFacade(key).mapAsList(list,targetClass);
    }

    public static <T, M> PageResponse<M> convertPage(Page<T> page, Class<M> targetClass) {
        if(page==null){
            return null;
        }
        PageResponse<M> pageResponse=new PageResponse<>();
        pageResponse.setTotal((int) page.getTotalRow());
        pageResponse.setList(convert(page.getRecords(),targetClass));
        pageResponse.setPageNum((int) page.getPageNumber());
        pageResponse.setPageSize((int) page.getPageSize());
        pageResponse.setTotalPage((int) page.getTotalPage());
        return pageResponse;
    }



    private static MapperFacade getMapperFacade(String key){
        MapperFacade mapperFacade= getCacheMapperFacadeMap().get(key);
        if(mapperFacade==null) {
            synchronized (ConvertUntil.class) {
                mapperFacade= getCacheMapperFacadeMap().get(key);
                if(mapperFacade==null) {
                    mapperFacade = mapperFactory.getMapperFacade();
                    setMapperFacade(key, mapperFacade);
                }
            }
        }
        return mapperFacade;
    }

    private static  String getMapperKey(Class sourceClass, Class targetClass){
        return sourceClass.getName()+":"+targetClass.getName();
    }

    private static void setMapperFacade(String key,MapperFacade mapperFacade){
        CACHE_MAPPER_FACADE_MAP.put(key,mapperFacade);
    }




}
